﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class FindMissingWindow : EditorWindow
{
    //[MenuItem("Patcher/检查/检查MissingReference资源")]
    //public static void FindMissing()
    //{
    //    GetWindow<FindMissingWindow>().titleContent = new GUIContent("查找Missing资源");
    //    GetWindow<FindMissingWindow>().Show();
    //    Find();
    //}

    [MenuItem("Packager/工具/Find Build Crash prefabs")]
    public static bool FindCrashMissingPrefabs()
    {
        List<string> missingPrefabList = new List<string>();
        string[] allassetpaths = AssetDatabase.GetAllAssetPaths();
        return FindCrashMissingPrefabs(allassetpaths);
    }

    /// <summary>
    /// 查找是否有依赖资源丢失
    /// </summary>
    /// <param name="allassetpaths"></param>
    /// <returns></returns>
    public static bool FindCrashMissingPrefabs(string[] allassetpaths)
    {
        List<string> missingPrefabList = new List<string>();
        List<string> oldAssetList = new List<string>();
        EditorUtility.DisplayProgressBar("Bundle Crash Find", "Finding...", 0f);
        int len = allassetpaths.Length;
        int index = 0;
        foreach (var filePath in allassetpaths)
        {
            if(!filePath.Contains("GameAssets"))
            {
                index++;
                continue;
            }
            EditorUtility.DisplayProgressBar("Bundle Crash Find", filePath, (index + 0f) / (len + 0f));
            if (filePath.EndsWith(".prefab"))
            {
                GameObject fileObj = PrefabUtility.LoadPrefabContents(filePath);
                if (fileObj)
                {
                    Component[] cps = fileObj.GetComponentsInChildren<Component>(true);
                    foreach (var cp in cps)
                    {
                        if (cp)
                        {
                            PrefabInstanceStatus _type = PrefabUtility.GetPrefabInstanceStatus(cp.gameObject);
                            if (_type == PrefabInstanceStatus.MissingAsset)
                            {
                                if (missingPrefabList.Contains(filePath))
                                    continue;
                                missingPrefabList.Add(filePath);
                                //string nodePath = PsdToUguiEx.CopyLuJin(null)+"/"+ fileObj.name;
                                //Debug.LogError("Crash Bundle Missing Prefab:Path=" + filePath + " Name:" + fileObj.name + " ComponentName:" + cp);
                            }
                        }
                    }
                }
                PrefabUtility.UnloadPrefabContents(fileObj);
            }
            index++;
        }

        if(missingPrefabList.Count <= 0)
        {
            Debug.Log("Prefab没有Miss 资源正常..");
            EditorUtility.ClearProgressBar();
            return false;
        }

        int i = Application.dataPath.LastIndexOf('/');
        string logPath = Application.dataPath.Substring(0, i + 1) + "log/";
        //if (Directory.Exists(logPath))
        //{
        //    Directory.Delete(logPath, true);
        //}
        if (!Directory.Exists(logPath))
        {
            Directory.CreateDirectory(logPath);
        }
        string logFilePath = string.Concat(logPath, string.Format("{0}.txt", "BuildCrash"));
        if (File.Exists(logFilePath))
        {
            File.Delete(logFilePath);
        }
        try
        {
            FileStream fs = new FileStream(logFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
            StreamWriter sw = new StreamWriter(fs);
            missingPrefabList.Sort();
            foreach (var v in missingPrefabList)
            {
                string msg = "Crash Bundle Missing Prefab:Path= " + v;
                Debug.LogError(msg);
                if (sw != null)
                {
                    sw.WriteLine(msg);
                    sw.Flush();
                }
            }
            sw.Close();
            sw.Dispose();
            fs.Close();
            fs.Dispose();
        }
        catch (Exception ex)
        {
            UnityEngine.Debug.LogError(ex.Message);
        }

        EditorUtility.ClearProgressBar();
        if (missingPrefabList.Count > 0)
        {
            return true;
        }
        return false;
    }


    private static Dictionary<UnityEngine.Object, List<UnityEngine.Object>> prefabs = new Dictionary<UnityEngine.Object, List<UnityEngine.Object>>();
    private static Dictionary<UnityEngine.Object, string> refPaths = new Dictionary<UnityEngine.Object, string>();

    private static void Find()
    {
        prefabs.Clear();
        string[] allassetpaths = AssetDatabase.GetAllAssetPaths();//获取所有资源路径
        var gos = allassetpaths
            .Where(a => a.EndsWith("prefab"))//筛选 是以prefab为后缀的 预设体资源
            .Select(a => AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(a));//加载这个预设体
        //gos拿到的是所有加载好的预设体
        foreach (var item in gos)
        {
            GameObject go = item as GameObject;
            if (go)
            {
                Component[] cps = go.GetComponentsInChildren<Component>(true);//获取这个物体身上所有的组件
                foreach (var cp in cps)//遍历每一个组件
                {
                    if (!cp)
                    {
                        if (!prefabs.ContainsKey(go))
                        {
                            prefabs.Add(go, new List<UnityEngine.Object>() { cp });
                        }
                        else
                        {
                            prefabs[go].Add(cp);
                        }
                        continue;
                    }
                    SerializedObject so = new SerializedObject(cp);//生成一个组件对应的Object对象 用于遍历这个组件的所有属性
                    var iter = so.GetIterator();//拿到迭代器
                    while (iter.NextVisible(true))//如果有下一个属性
                    {
                        //如果这个属性类型是引用类型的
                        if (iter.propertyType == SerializedPropertyType.ObjectReference)
                        {
                            //引用对象是null 并且 引用ID不是0 说明丢失了引用
                            if (iter.objectReferenceValue == null && iter.objectReferenceInstanceIDValue != 0)
                            {
                                if (!refPaths.ContainsKey(cp))
                                    refPaths.Add(cp, iter.propertyPath);
                                else
                                    refPaths[cp] += " | " + iter.propertyPath;
                                if (prefabs.ContainsKey(go))
                                {
                                    if (!prefabs[go].Contains(cp)) prefabs[go].Add(cp);
                                }
                                else
                                {
                                    prefabs.Add(go, new List<UnityEngine.Object>() { cp });
                                }
                            }
                        }
                    }
                }
            }
        }
        EditorUtility.DisplayDialog("", "就绪", "OK");
    }

    //以下只是将查找结果显示
    private Vector3 scroll = Vector3.zero;
    private void OnGUI()
    {
        scroll = EditorGUILayout.BeginScrollView(scroll);
        EditorGUILayout.BeginVertical();
        //if (GUILayout.Button("移除丢失引用的Animator组件"))
        //    RemoveAnimatorWithMissReference();
        if(prefabs.Count <= 0)
        {
            GUILayout.Label("没有丢失引用或Miss Prefab");
        }

        foreach (var item in prefabs)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.ObjectField(item.Key, typeof(GameObject), true, GUILayout.Width(200));
            EditorGUILayout.BeginVertical();
            foreach (var cp in item.Value)
            {
                if (cp)
                {
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.ObjectField(cp, cp.GetType(), true, GUILayout.Width(200));
                    if (refPaths.ContainsKey(cp))
                    {
                        GUILayout.Label(refPaths[cp]);
                    }
                    EditorGUILayout.EndHorizontal();
                }
            }
            EditorGUILayout.EndVertical();
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndVertical();
        EditorGUILayout.EndScrollView();
    }

    private void RemoveAnimatorWithMissReference()
    {
        int count = 0;
        foreach (var cps in prefabs.Values)
        {
            foreach (var item in cps)
            {
                Animator a = item as Animator;
                if (a)
                {
                    count++;
                    EditorUtility.SetDirty(a.gameObject);
                    DestroyImmediate(a, true);
                }
            }
        }
        if (count > 0)
        {
            EditorUtility.DisplayDialog("", "一共移除" + count + "个Animator组件", "OK");
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            Find();
        }
    }
}